#include <iostream>
#include <unistd.h>
#include <vector>
#include <pthread.h>
#include <semaphore.h>
#include <cstdlib>
#include <time.h>
#include <cstdio>
using namespace std;
template <class T>
class RingQueue
{
private:
    int _capacity;
    vector<T> _arry;
    int _WritePos;
    int _ReadPos;
    sem_t _data;
    sem_t _block;
    sem_t _lock;

public:
    RingQueue(int cap)
        : _capacity(cap), _arry(cap), _WritePos(0), _ReadPos(0)
    {
        sem_init(&_data, 0, 0);
        sem_init(&_block, 0, cap);
        sem_init(&_lock, 0, 1);
    }

    ~RingQueue()
    {
        sem_destroy(&_data);
        sem_destroy(&_block);
        sem_destroy(&_lock);
    }

    bool Push(const T &data)
    {
        sem_wait(&_block);
        sem_wait(&_lock);
        _arry[_WritePos] = data;
        ++_WritePos;
        _WritePos %= _capacity;
        sem_post(&_lock);
        sem_post(&_data);
        return true;
    }

    bool Pop(T *data)
    {
        sem_wait(&_data);
        sem_wait(&_lock);
        *data = _arry[_ReadPos];
        ++_ReadPos;
        _ReadPos %= _capacity;
        sem_post(&_lock);
        sem_post(&_block);
        return true;
    }
};

void *product(void *args)
{
    RingQueue<int> *rq = (RingQueue<int> *)args;
    srand(time(0));
    while (true)
    {
        int data = rand() % 10 + 1;
        rq->Push(data);
        printf("%d :push %d\n", pthread_self, data);
    }
    return NULL;
}

void *consumer(void *args)
{
    RingQueue<int> *rq = (RingQueue<int> *)args;
    while (true)
    {
        int data = 0;
        rq->Pop(&data);
        printf("%d :pop %d\n", pthread_self, data);
    }
    return NULL;
}

int main()
{
    RingQueue<int> rq(5);
    pthread_t pro[3];
    pthread_t con[4];

    for (int i = 0; i < 3; ++i)
    {
        pthread_create(pro + i, NULL, product, (void *)&rq);
    }

    for (int i = 0; i < 4; ++i)
    {
        pthread_create(con + i, NULL, consumer, (void *)&rq);
    }

    for (int i = 0; i < 3; ++i)
    {
        pthread_join(pro[i], NULL);
    }

    for (int i = 0; i < 4; ++i)
    {
        pthread_join(con[i], NULL);
    }
    return 0;
}
